package com.gitee.wsl.compose.modifier.style


import androidx.compose.foundation.background
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import com.gitee.wsl.common.ui.base.ColorOrBrush
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.composed
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PointMode
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.unit.dp
import com.gitee.wsl.base.Or
import com.gitee.wsl.compose.modifier.ModifierStyleScope
import kotlin.math.roundToInt

internal fun Modifier.background(colorOrBrush: ColorOrBrush,shape: Shape = RectangleShape/*, @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1.0f*/): Modifier{
    return when(colorOrBrush){
        is Or.One -> background(colorOrBrush.value,shape)
        is Or.Two -> background(colorOrBrush.value,shape)
    }
}

internal fun Modifier.horizontalGradientBackground(
    colors: List<Color>
) = gradientBackground(colors) { gradientColors, size ->
    Brush.horizontalGradient(
        colors = gradientColors,
        startX = 0f,
        endX = size.width
    )
}

internal fun Modifier.verticalGradientBackground(
    colors: List<Color>
) = gradientBackground(colors) { gradientColors, size ->
    Brush.verticalGradient(
        colors = gradientColors,
        startY = 0f,
        endY = size.width
    )
}

internal fun Modifier.diagonalGradientTint(
    colors: List<Color>,
    blendMode: BlendMode
) = gradientTint(colors, blendMode) { gradientColors, size ->
    Brush.linearGradient(
        colors = gradientColors,
        start = Offset(
            x = 0f,
            y = 0f
        ),
        end = Offset(
            x = size.width,
            y = size.height
        ),
        tileMode = TileMode.Clamp
    )
}

internal fun Modifier.gradientBackground(
    colors: List<Color>,
    brushProvider: (List<Color>, Size) -> Brush
): Modifier = composed {
    var size by remember { mutableStateOf(Size.Zero) }
    val gradient = remember(colors, size) { brushProvider(colors, size) }
    drawWithContent {
        size = this.size
        drawRect(brush = gradient)
        drawContent()
    }
}

internal fun Modifier.gradientTint(
    colors: List<Color>,
    blendMode: BlendMode,
    brushProvider: (List<Color>, Size) -> Brush
) = composed {
    var size by remember { mutableStateOf(Size.Zero) }
    val gradient = remember(colors, size) { brushProvider(colors, size) }
    drawWithContent {
        drawContent()
        size = this.size
        drawRect(
            brush = gradient,
            blendMode = blendMode
        )
    }
}

internal fun Modifier.offsetGradientBackground(
    colors: List<Color>,
    width: Float,
    offset: Float = 0f
) = background(
    Brush.horizontalGradient(
        colors,
        startX = -offset,
        endX = width - offset,
        tileMode = TileMode.Mirror
    )
)

fun ModifierStyleScope.offsetGradientBackground(
    colors: List<Color>,
    width: Float,
    offset: Float = 0f
){
    modifier = modifier.offsetGradientBackground(colors, width, offset)
}

fun ModifierStyleScope.gradientTint(
    colors: List<Color>,
    blendMode: BlendMode,
    brushProvider: (List<Color>, Size) -> Brush
){
    modifier=modifier.gradientTint(colors, blendMode, brushProvider)
}

fun ModifierStyleScope.gradientBackground(
    colors: List<Color>,
    brushProvider: (List<Color>, Size) -> Brush
){
    modifier = modifier.gradientBackground(colors, brushProvider)
}

fun ModifierStyleScope.diagonalGradientTint(
    colors: List<Color>,
    blendMode: BlendMode
){
    modifier=modifier.diagonalGradientTint(colors, blendMode)
}

fun ModifierStyleScope.horizontalGradientBackground(
    colors: List<Color>
){
    modifier=modifier.horizontalGradientBackground(colors)
}

fun ModifierStyleScope.verticalGradientBackground(
    colors: List<Color>
){
    modifier=modifier.verticalGradientBackground(colors)
}

/**
 * Used to erase the participant lines behind labels with transparent backgrounds.
 */
internal fun Modifier.clearBackground(): Modifier = drawBehind {
    drawRect(Color.Black, blendMode = BlendMode.Clear)
}

fun ModifierStyleScope.clearBackground(){
    modifier = modifier.clearBackground()
}

fun Modifier.alphaBackground(shape: Shape = RectangleShape, enabled: Boolean = true) =
    clip(shape).drawWithCache {
        if (!enabled) return@drawWithCache onDrawBehind { }
        val strokeSize = 4.dp.toPx()
        val pointCenter = strokeSize / 2
        val count = (size.width / strokeSize).roundToInt()
        val firstLineCount = (count / 2f).toInt()
        val secondLineCount = ((count + 1) / 2f).toInt()
        val lineCount = (size.height / strokeSize).roundToInt()
        val itemCount = (lineCount * (firstLineCount + secondLineCount) / 2f + 0.5f).toInt()
        val points = List(itemCount) {
            val index = it.mod(firstLineCount + secondLineCount)
            val lineIndex = it / (firstLineCount + secondLineCount)
            when (index) {
                in 0 until firstLineCount -> Offset(
                    x = pointCenter + (index * 2 + 1) * strokeSize,
                    y = pointCenter + 2 * lineIndex * strokeSize
                )

                else -> Offset(
                    x = pointCenter + (index - firstLineCount) * 2 * strokeSize,
                    y = pointCenter + (2 * lineIndex + 1) * strokeSize
                )
            }
        }
        onDrawBehind {
            drawPoints(
                points = points,
                color = Color.Gray.copy(0.2f),
                strokeWidth = strokeSize,
                cap = StrokeCap.Square,
                pointMode = PointMode.Points
            )
        }
    }

